/*
 * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package com.itcast;

import org.openjdk.jmh.annotations.*;

import java.util.Arrays;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;

/**
 *  相关JMH： https://www.163.com/dy/article/GOS78LCE0531A0HG.html
 * @Warmup(
 * iterations = 5,
 * time = 1,
 * timeUnit = TimeUnit.SECONDS)
 * 意思是对代码预热总计5秒（迭代5次，每次一秒） 。预热过程的测试数据，是不记录测量结果的。
 *  timeUnit：时间的单位，默认的单位是秒。
 *  iterations：预热阶段的迭代数。
 *  time：每次预热的时间。
 *  batchSize：批处理大小，指定了每次操作调用几次方法。
 *
 * @Measurement(
 * iterations = 5,
 * time = 1,
 * timeUnit = TimeUnit.SECONDS)
 * Measurement和Warmup的参数是一样的。不同于预热，它指的是真正的迭代次数。
 *
 * @BenchmarkMode
 * 此注解用来指定基准测试类型，对应Mode选项，用来修饰类和方法都可以。这里的value，是一个数组，可以配置多个统计维度。
 * @BenchmarkMode({Throughput,Mode.AverageTime})。统计的就是吞吐量和平均执行时间两个指标。
 *  Throughput：整体吞吐量，比如QPS，单位时间内的调用量等。
 *  AverageTime：平均耗时，指的是每次执行的平均时间。如果这个值很小不好辨认，可以把统计的单位时间调小一点。
 *  SampleTime：随机取样。
 *  SingleShotTime：如果你想要测试仅仅一次的性能，比如第一次初始化花了多长时间，就可以使用这个参数，其实和传统的main方法没有什么区别。
 *  All：所有的指标，都算一遍，你可以设置成这个参数看下效果。
 * 可以看到，在衡量这些指标的时候，都有一个时间维度，它就是通过**@OutputTimeUnit**注解进行配置的。
 * OutputTimeUnit注解同样可以修饰类或者方法，通过更改时间级别，可以获取更加易读的结果。
 *
 * @Fork
 * fork的值一般设置成1，表示只使用一个进程进行测试；
 * 如果这个数字大于1，表示会启用新的进程进行测试；
 * 但如果设置成0，程序依然会运行，不过这样是在用户的JVM进程上运行的，
 * 可以看下下面的提示，但不推荐这么做。
 *
 * 其实fork注解有一个参数叫做jvmArgsAppend，我们可以通过它传递一些JVM的参数。
 * @Fork(value = 3, jvmArgsAppend = {"-Xmx2048m", "-server", "-XX:+AggressiveOpts"})
 *
 * @Threads
 * fork是面向进程的，而Threads是面向线程的。指定了这个注解以后，将会开启并行测试。
 * 如果配置了 Threads.MAX ，则使用和处理机器核数相同的线程数。
 *
 * @Group
 * @Group注解只能加在方法上，用来把测试方法进行归类。如果你单个测试文件中方法比较多，或者需要将其归类，则可以使用这个注解。
 * 与之关联的@GroupThreads注解，会在这个归类的基础上，再进行一些线程方面的设置。
 *
 * @State
 * @State 指定了在类中变量的作用范围。它有三个取值。
 * @State 用于声明某个类是一个“状态”，可以用Scope 参数用来表示该状态的共享范围。这个注解必须加在类上，否则提示无法运行。
 * Scope有如下3种值：
 * Benchmark：表示变量的作用范围是某个基准测试类。
 * Thread：每个线程一份副本，如果配置了Threads注解，则每个Thread都拥有一份变量，它们互不影响。
 * Group：联系上面的@Group注解，在同一个Group里，将会共享同一个变量实例。
 *
 */

@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Fork(1)
@BenchmarkMode(Mode.AverageTime)    // 测试模式：统计平均次数
@Warmup(iterations=3)   // 热身次数3次
@Measurement(iterations=5)  // 表示进行五轮测试
public class MyBenchmark {
    static int[] ARRAY = new int[1000_000_00];
    static {
        Arrays.fill(ARRAY, 1);
    }
    @Benchmark
    public int c() throws Exception {
        int[] array = ARRAY;
        FutureTask<Integer> t1 = new FutureTask<>(()->{
            int sum = 0;
            for(int i = 0; i < 250_000_00;i++) {
                sum += array[0+i];
            }
            return sum;
        });
        FutureTask<Integer> t2 = new FutureTask<>(()->{
            int sum = 0;
            for(int i = 0; i < 250_000_00;i++) {
                sum += array[250_000_00+i];
            }
            return sum;
        });
        FutureTask<Integer> t3 = new FutureTask<>(()->{
            int sum = 0;
            for(int i = 0; i < 250_000_00;i++) {
                sum += array[500_000_00+i];
            }
            return sum;
        });
        FutureTask<Integer> t4 = new FutureTask<>(()->{
            int sum = 0;
            for(int i = 0; i < 250_000_00;i++) {
                sum += array[750_000_00+i];
            }
            return sum;
        });
        new Thread(t1).start();
        new Thread(t2).start();
        new Thread(t3).start();
        new Thread(t4).start();
        return t1.get() + t2.get() + t3.get()+ t4.get();
    }
    @Benchmark
    public int d() throws Exception {
        int[] array = ARRAY;
        FutureTask<Integer> t1 = new FutureTask<>(()->{
            int sum = 0;
            for(int i = 0; i < 1000_000_00;i++) {
                sum += array[0+i];
            }
            return sum;
        });
        new Thread(t1).start();
        return t1.get();
    }
}
